// VMExplorer.Debugger.h

#pragma once

using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Diagnostics;

namespace VMExplorer {

	public ref class Debugger
	{
	private:
		IDebugClient *client;
    IDebugControl *control;
    IDebugSymbols *symbols;

	public:
		Debugger()
		{
	    HRESULT hr = E_FAIL;

			try
			{
				// Initialize COM
				hr = CoInitialize(NULL);
				if(FAILED(hr)) throw gcnew Exception();
		 
				LPVOID pvTemp;
				// Create the base IDebugClient object
				hr = DebugCreate(__uuidof(IDebugClient), (LPVOID*)&pvTemp);
				if(FAILED(hr)) throw gcnew Exception();
				client = (IDebugClient*)pvTemp;
		 
				// from the base, create the Control and Symbols objects
				hr = client->QueryInterface(__uuidof(IDebugControl), (LPVOID*)&pvTemp);
				if(FAILED(hr)) throw gcnew Exception();
				control = (IDebugControl*)pvTemp;
		 
				hr = client->QueryInterface(__uuidof(IDebugSymbols), (LPVOID*)&pvTemp);
				if(FAILED(hr)) throw gcnew Exception();
				symbols = (IDebugSymbols*)pvTemp;
			}
			finally
			{
			}
		}

		virtual ~Debugger()
		{
			// cleanup and destroy the objects
			if(symbols)
			{
				symbols->Release();
				symbols = NULL;
			}
			if(control)
			{
				control->Release();
				control = NULL;
			}
			if(client)
			{
				client->Release();
				client = NULL;
			}

			// cleanup COM
			CoUninitialize();
		}

		bool OpenDumpFile(String^ dmpPath)
		{
			// open the crash dump
			IntPtr ip = Marshal::StringToHGlobalAnsi(dmpPath);
			HRESULT hr = client->OpenDumpFile(static_cast<const char*>(ip.ToPointer()));
			Marshal::FreeHGlobal(ip);
			if(FAILED(hr)) return false;

			// wait for the engine to finish processing
			control->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE);

			return true;
 		}

		bool DumpStack()
		{
			HRESULT hr = S_OK;
			DEBUG_STACK_FRAME stackFrames[4096] = {0};
			ULONG numFrames = 0;

			// get the stack trace
			hr = control->GetStackTrace(0, 0, 0, stackFrames, ARRAYSIZE(stackFrames), &numFrames);
			if(FAILED(hr)) return false;

			char pBuffer[1024] = {0};
			OutputDebugString("\nStack Trace\n");

			// dump out the details for each function call
			for(ULONG frame=0; frame<numFrames; frame++)
			{
				HRESULT symhr;
				char name[512];
				unsigned __int64 offset = 0;
				ULONG lineNo = 0;

				// get the module, function, and offset
				ZeroMemory(name, ARRAYSIZE(name));
				symhr = symbols->GetNameByOffset(stackFrames[frame].InstructionOffset, name, ARRAYSIZE(name)-1, NULL, &offset);

				if(SUCCEEDED(symhr))
				{
					sprintf(pBuffer, "%s+0x%I64X", name, offset);
				}
				else
				{
					sprintf(pBuffer, "0x08%I64X", stackFrames[frame].InstructionOffset);
				}
				OutputDebugString(pBuffer);

				// look for source file name and line number
				ZeroMemory(name, ARRAYSIZE(name));
				symhr = symbols->GetLineByOffset(stackFrames[frame].InstructionOffset, &lineNo, name, ARRAYSIZE(name)-1, NULL, NULL);

				if(SUCCEEDED(symhr))
				{
					sprintf(pBuffer, "\n     %s(%u)", name, lineNo);
					OutputDebugString(pBuffer);
				}
				OutputDebugString("\n");
			}

			return true;
		}	
	};
}

